﻿<script setup lang="ts" name="UploadSource">
import type { UploadProps } from 'element-plus'
import { useUpload } from './useUpload'

const { uploadUrl, httpRequest } = useUpload()

const message = useMessage() // 消息弹窗
const { getPrefixCls } = useDesign()

const prefixCls = getPrefixCls('upload-source')
const { t } = useI18n() // 国际化

const emit = defineEmits(['update:modelValue'])

const props = defineProps({
  width: {
    type: [String, Number],
    default: '160px'
  },
  modelValue: { type: String },
  accept: {
    type: String,
    required: true
  },
  maxSize: {
    type: Number,
    required: true
  }
})

const initInfo = ref({
  isPreview: false,
  list: props.modelValue ? [props.modelValue] : [],
  type: '',
  fileTypeArr: ['7z', 'ai', 'cdr', 'class', 'css', 'csv', 'doc', 'dockerfile', 'docx', 'exe', 'gitignore', 'html', 'iso', 'jar', 'java', 'js', 'json', 'md', 'pdf', 'ppt', 'pptx', 'pr', 'psd', 'py', 'rar', 'sql', 'svg', 'txt', 'xls', 'xlsx', 'xml', 'yml', 'zip']
})

watch(
  () => props.modelValue,
  (val: any) => {
    const index = val.lastIndexOf('.')
    const type = val.substring(index + 1)
    if (type === 'jpg' || type === 'jpeg' || type === 'png' || type === 'gif' || type === 'bmp') {
      initInfo.value.type = 'image'
    } else if (type === 'mp4' || type === 'rmvb' || type === 'avi' || type === 'ts') {
      initInfo.value.type = 'video'
    } else if (type === 'mp3') {
      initInfo.value.type = 'audio'
    } else {
      initInfo.value.type = type
    }
    initInfo.value.list = [val]
  },
  {
    immediate: true
  }
)

const newwidth = computed(() => `${props.width}`.replace(/px/, '') + 'px')

// 文件上传前置方法
const beforeUploadSource = (file) => {
  if (props.maxSize > 0) {
    const isMaxSize = file.size / 1024 < props.maxSize
    if (!isMaxSize) {
      message.error(t('upload', 'maxSizeMultiple', [String(props.maxSize)]))
    }
    return isMaxSize
  }
}

// 删除
const deleteCover = () => {
  emit('update:modelValue', '')
}

// 预览
const previewCover = () => {
  initInfo.value.isPreview = true
}

// 关闭预览
const closePreview = () => {
  initInfo.value.isPreview = false
}

// 图片上传成功提示
const uploadSuccess: UploadProps['onSuccess'] = (res: any): void => {
  message.success('上传成功')
  emit('update:modelValue', res.data)
}

// 图片上传错误提示
const uploadError = () => {
  message.notifyError('图片上传失败，请您重新上传！')
}
</script>

<template>
  <div :class="prefixCls">
    <div v-if="props.modelValue" :class="`${prefixCls}-preview`">
      <div class="mask">
        <SvgIcon v-if="initInfo.type === 'image'" icon="ri:eye-line" size="18" :title="t('action', 'preview')" @click="previewCover" />
        <ElUpload :action="uploadUrl" :accept="props.accept" :show-file-list="false" :before-upload="beforeUploadSource" :http-request="httpRequest" :on-error="uploadError" :on-success="uploadSuccess">
          <SvgIcon icon="ri:exchange-line" size="18" :title="t('upload', 'replace')" />
        </ElUpload>
        <SvgIcon icon="ri:delete-bin-5-line" size="18" :title="t('action', 'remove')" @click="deleteCover" />
      </div>
      <template v-if="initInfo.type === 'image'">
        <ElImage :src="props.modelValue" fit="fill" />
        <ElImageViewer v-if="initInfo.isPreview" :url-list="initInfo.list" :initial-index="0" :hide-on-click-modal="true" @close="closePreview" />
      </template>
      <SvgIcon v-else-if="initInfo.type === 'video'" icon="ci:filetype-video" size="64" />
      <SvgIcon v-else-if="initInfo.type === 'audio'" icon="ci:filetype-audio" size="64" />
      <template v-else>
        <SvgIcon v-if="initInfo.fileTypeArr.includes(initInfo.type)" :icon="'ci:filetype-' + initInfo.type" size="64" />
        <SvgIcon v-else icon="ci:filetype-unknown" size="64" />
      </template>
    </div>
    <ElUpload v-else :class="`${prefixCls}-new`" :action="uploadUrl" :accept="props.accept" :show-file-list="false" :before-upload="beforeUploadSource" :http-request="httpRequest" :on-error="uploadError" :on-success="uploadSuccess">
      <SvgIcon icon="ep:plus" size="28" color="" />
    </ElUpload>
    <div v-if="props.maxSize > 0" :class="`${prefixCls}-tips`">
      <p v-html="t('upload', 'maxSizeMultiple', [String(props.maxSize)])" />
    </div>
  </div>
</template>

<style scoped lang="scss">
$prefix-cls: #{$namespace}-upload-source;
.#{$prefix-cls} {
  display: flex;
  flex: 1;
  padding: 6px;

  &-preview {
    position: relative;
    display: flex;
    width: v-bind(newwidth);
    height: v-bind(newwidth);
    overflow: hidden;
    background-color: var(--el-fill-color-lighter);
    border: 1px solid var(--el-border-color-darker);
    border-radius: 6px;
    box-sizing: border-box;
    align-items: center;
    justify-content: center;

    &:hover {
      .mask {
        opacity: 1;
        visibility: visible;
      }
    }

    .mask {
      position: absolute;
      top: 0;
      left: 0;
      z-index: 10;
      display: flex;
      width: 100%;
      height: 100%;
      background: rgba(0, 0, 0, 0.5);
      opacity: 0;
      visibility: hidden;
      transition: all 0.3s;
      align-items: center;
      justify-content: center;

      >div {
        line-height: 0;
      }

      .el-icon {
        margin: 0 6px;
        font-size: 24px !important;
        color: #fff;
        cursor: pointer;
        transition: all 0.3s;

        &:hover {
          font-size: 32px !important;
        }
      }
    }

    .el-image,
    .el-image :deep(.el-image__inner) {
      max-width: calc(v-bind(newwidth) - 2px);
      max-height: calc(v-bind(newwidth) - 2px);
    }
  }

  &-new {
    display: flex;
    width: v-bind(newwidth);
    height: v-bind(newwidth);
    cursor: pointer;
    background-color: var(--el-fill-color-lighter);
    border: 1px dashed var(--el-border-color-darker);
    border-radius: 6px;
    box-sizing: border-box;
    align-items: center;
    justify-content: center;

    &:hover {
      border-color: var(--el-color-primary);
    }

    :deep(.el-upload) {
      flex: 1;
      height: 100%;
    }
  }

  &-tips {
    display: flex;
    flex: 1;
    flex-direction: column;
    align-items: flex-start;
    justify-content: flex-end;
    padding-left: 10px;

    p {
      font-size: 12px;
      line-height: 24px;
      color: var(--el-text-color-secondary);
    }
  }
}
</style>
